#include "multih_server.h"
#include "http.h"

#include <iostream>

#include <sys/socket.h>
#include <sys/types.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>

TMultiHttpFileServer::TMultiHttpFileServer()
: Connections()
, Handlers()
, Socket(-1)
{
}

bool TMultiHttpFileServer::Init(const TOptions& options)
{
    GetHttpStatusMessages();

    bzero(&IgnoreSIGPIPE, sizeof(IgnoreSIGPIPE));
    IgnoreSIGPIPE.sa_handler    =   SIG_IGN;
    if (0 != sigaction(SIGPIPE, &IgnoreSIGPIPE, NULL)) {
        return false;
    }

    addrinfo    serverAddress;
    bzero(static_cast<void*>(&serverAddress), sizeof(serverAddress));
    serverAddress.ai_family     =   AF_UNSPEC;
    serverAddress.ai_socktype   =   SOCK_STREAM;
    serverAddress.ai_flags      =   AI_PASSIVE;

    addrinfo*   availibleAdressses;

    if (0 != getaddrinfo(NULL, options.Port.c_str(), &serverAddress, &availibleAdressses)) {
        std::cerr << "could not get addrinfo for " << options.Port << std::endl;
        return false;
    }

    addrinfo*   infoIter        =   availibleAdressses;
    while ((infoIter != NULL) && (Socket < 0)) {
        Socket  =   socket(infoIter->ai_family, infoIter->ai_socktype, infoIter->ai_protocol);
        if (Socket < 0) {
            infoIter    =   infoIter->ai_next;
            continue;
        }
        int setOn   =   1;
        if (setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, &setOn, sizeof(setOn)) < 0) {
            infoIter    =   infoIter->ai_next;
            close(Socket);
            Socket      =   -1;
            continue;
        }
        if (bind(Socket, infoIter->ai_addr, infoIter->ai_addrlen) < 0) {
            infoIter    =   infoIter->ai_next;
            close(Socket);
            Socket      =   -1;
            continue;
        }
    }
    freeaddrinfo(availibleAdressses);

    if (Socket < 0) {
        std::cerr << "could not bind socket for port " << options.Port << std::endl;
        return false;
    }

    listen(Socket, options.NetworkQueueSize);

    if (!Connections.Init(options.ConnectionQueueSize)) {
        std::cerr << "could not create connection manager with "
                  << options.ConnectionQueueSize << " unserved connection size" << std::endl;
        return false;
    }

    if (!Handlers.Init(options.PoolSize, Connections, options.Path, options.InternalBufferSize)) {
        std::cerr << "could not create worker pool with" << std::endl
                  << "\t" << options.PoolSize << " workers" << std::endl
                  << "\t" << options.InternalBufferSize << " bytes memory for each" << std::endl
                  << "\tserving " << options.Path << std::endl;
        return false;
    }
    return true;
}

void TMultiHttpFileServer::IoLoop(const TOptions& options)
{
    if (!Init(options)) {
        return;
    }

    int         connectionId    =   0;
    sockaddr    connectionAddress;
    bzero(static_cast<void*>(&connectionAddress), sizeof(connectionAddress));
    socklen_t   connectionAddressLen    =   sizeof(connectionAddress);
    while (true) {
        connectionId    =   accept( Socket
                                  , &connectionAddress
                                  , &connectionAddressLen );
        if (connectionId < 0) {
            continue;
        }
        Connections.AddConnection(connectionId);
    }
}

void TMultiHttpFileServer::Stop()
{
    Connections.Stop();
    Handlers.Stop();
}

TMultiHttpFileServer::~TMultiHttpFileServer()
{
    Stop();

    if (Socket >= 0) {
        close(Socket);
    }
}
